// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "gpu/command_buffer/service/gles2_cmd_decoder_unittest_base.h"

#include <algorithm>
#include <string>
#include <vector>

#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "gpu/command_buffer/common/gles2_cmd_format.h"
#include "gpu/command_buffer/common/gles2_cmd_utils.h"
#include "gpu/command_buffer/common/value_state.h"
#include "gpu/command_buffer/service/cmd_buffer_engine.h"
#include "gpu/command_buffer/service/context_group.h"
#include "gpu/command_buffer/service/logger.h"
#include "gpu/command_buffer/service/mailbox_manager.h"
#include "gpu/command_buffer/service/program_manager.h"
#include "gpu/command_buffer/service/test_helper.h"
#include "gpu/command_buffer/service/vertex_attrib_manager.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gl/gl_implementation.h"
#include "ui/gl/gl_mock.h"
#include "ui/gl/test/gl_surface_test_support.h"

using ::gfx::MockGLInterface;
using ::testing::_;
using ::testing::AnyNumber;
using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::Invoke;
using ::testing::InvokeWithoutArgs;
using ::testing::MatcherCast;
using ::testing::Pointee;
using ::testing::Return;
using ::testing::SetArgPointee;
using ::testing::SetArgumentPointee;
using ::testing::SetArrayArgument;
using ::testing::StrEq;
using ::testing::StrictMock;
using ::testing::WithArg;

namespace {

void NormalizeInitState(gpu::gles2::GLES2DecoderTestBase::InitState* init)
{
    CHECK(init);
    const char* kVAOExtensions[] = {
        "GL_OES_vertex_array_object",
        "GL_ARB_vertex_array_object",
        "GL_APPLE_vertex_array_object"
    };
    bool contains_vao_extension = false;
    for (size_t ii = 0; ii < arraysize(kVAOExtensions); ++ii) {
        if (init->extensions.find(kVAOExtensions[ii]) != std::string::npos) {
            contains_vao_extension = true;
            break;
        }
    }

    if (init->use_native_vao) {
        if (contains_vao_extension)
            return;
        if (!init->extensions.empty())
            init->extensions += " ";
        if (base::StartsWith(init->gl_version, "opengl es",
                base::CompareCase::INSENSITIVE_ASCII)) {
            init->extensions += kVAOExtensions[0];
        } else {
#if !defined(OS_MACOSX)
            init->extensions += kVAOExtensions[1];
#else
            init->extensions += kVAOExtensions[2];
#endif // OS_MACOSX
        }
    } else {
        // Make sure we don't set up an invalid InitState.
        CHECK(!contains_vao_extension);
    }

    if (!init->extensions.empty())
        init->extensions += " ";
    init->extensions += "GL_EXT_framebuffer_object ";
}

const uint32 kMaxColorAttachments = 16;
const uint32 kMaxDrawBuffers = 16;

} // namespace Anonymous

namespace gpu {
namespace gles2 {

    GLES2DecoderTestBase::GLES2DecoderTestBase()
        : surface_(NULL)
        , context_(NULL)
        , memory_tracker_(NULL)
        , client_buffer_id_(100)
        , client_framebuffer_id_(101)
        , client_program_id_(102)
        , client_renderbuffer_id_(103)
        , client_sampler_id_(104)
        , client_shader_id_(105)
        , client_texture_id_(106)
        , client_element_buffer_id_(107)
        , client_vertex_shader_id_(121)
        , client_fragment_shader_id_(122)
        , client_query_id_(123)
        , client_vertexarray_id_(124)
        , client_valuebuffer_id_(125)
        , client_transformfeedback_id_(126)
        , client_sync_id_(127)
        , service_renderbuffer_id_(0)
        , service_renderbuffer_valid_(false)
        , ignore_cached_state_for_test_(GetParam())
        , cached_color_mask_red_(true)
        , cached_color_mask_green_(true)
        , cached_color_mask_blue_(true)
        , cached_color_mask_alpha_(true)
        , cached_depth_mask_(true)
        , cached_stencil_front_mask_(static_cast<GLuint>(-1))
        , cached_stencil_back_mask_(static_cast<GLuint>(-1))
    {
        memset(immediate_buffer_, 0xEE, sizeof(immediate_buffer_));
    }

    GLES2DecoderTestBase::~GLES2DecoderTestBase() { }

    void GLES2DecoderTestBase::SetUp()
    {
        InitState init;
        // Autogenerated tests do not overwrite version or extension string,
        // so we have to pick something that supports everything here.
        init.gl_version = "4.4";
        init.extensions += " GL_ARB_compatibility";
        init.has_alpha = true;
        init.has_depth = true;
        init.request_alpha = true;
        init.request_depth = true;
        init.bind_generates_resource = true;
        InitDecoder(init);
    }

    void GLES2DecoderTestBase::AddExpectationsForVertexAttribManager()
    {
        for (GLint ii = 0; ii < kNumVertexAttribs; ++ii) {
            EXPECT_CALL(*gl_, VertexAttrib4f(ii, 0.0f, 0.0f, 0.0f, 1.0f))
                .Times(1)
                .RetiresOnSaturation();
        }
    }

    GLES2DecoderTestBase::InitState::InitState()
        : extensions("GL_EXT_framebuffer_object")
        , gl_version("2.1")
        , has_alpha(false)
        , has_depth(false)
        , has_stencil(false)
        , request_alpha(false)
        , request_depth(false)
        , request_stencil(false)
        , bind_generates_resource(false)
        , lose_context_when_out_of_memory(false)
        , use_native_vao(true)
        , context_type(CONTEXT_TYPE_OPENGLES2)
    {
    }

    void GLES2DecoderTestBase::InitDecoder(const InitState& init)
    {
        InitDecoderWithCommandLine(init, NULL);
    }

    void GLES2DecoderTestBase::InitDecoderWithCommandLine(
        const InitState& init,
        const base::CommandLine* command_line)
    {
        InitState normalized_init = init;
        NormalizeInitState(&normalized_init);
        // For easier substring/extension matching
        DCHECK(normalized_init.extensions.empty() || *normalized_init.extensions.rbegin() == ' ');
        gfx::SetGLGetProcAddressProc(gfx::MockGLInterface::GetGLProcAddress);
        gfx::GLSurfaceTestSupport::InitializeOneOffWithMockBindings();

        gl_.reset(new StrictMock<MockGLInterface>());
        ::gfx::MockGLInterface::SetGLInterface(gl_.get());

        SetupMockGLBehaviors();

        scoped_refptr<FeatureInfo> feature_info;
        if (command_line)
            feature_info = new FeatureInfo(*command_line);
        group_ = scoped_refptr<ContextGroup>(
            new ContextGroup(NULL, memory_tracker_, new ShaderTranslatorCache,
                new FramebufferCompletenessCache, feature_info.get(),
                new SubscriptionRefSet, new ValueStateMap,
                normalized_init.bind_generates_resource));
        bool use_default_textures = normalized_init.bind_generates_resource;

        InSequence sequence;

        surface_ = new gfx::GLSurfaceStub;
        surface_->SetSize(gfx::Size(kBackBufferWidth, kBackBufferHeight));

        // Context needs to be created before initializing ContextGroup, which will
        // in turn initialize FeatureInfo, which needs a context to determine
        // extension support.
        context_ = new StrictMock<GLContextMock>();
        context_->AddExtensionsString(normalized_init.extensions.c_str());
        context_->SetGLVersionString(normalized_init.gl_version.c_str());

        context_->GLContextStubWithExtensions::MakeCurrent(surface_.get());
        gfx::GLSurfaceTestSupport::InitializeDynamicMockBindings(context_.get());

        TestHelper::SetupContextGroupInitExpectations(
            gl_.get(),
            DisallowedFeatures(),
            normalized_init.extensions.c_str(),
            normalized_init.gl_version.c_str(),
            normalized_init.bind_generates_resource);

        // We initialize the ContextGroup with a MockGLES2Decoder so that
        // we can use the ContextGroup to figure out how the real GLES2Decoder
        // will initialize itself.
        mock_decoder_.reset(new MockGLES2Decoder());

        // Install FakeDoCommands handler so we can use individual DoCommand()
        // expectations.
        EXPECT_CALL(*mock_decoder_, DoCommands(_, _, _, _)).WillRepeatedly(Invoke(mock_decoder_.get(), &MockGLES2Decoder::FakeDoCommands));

        EXPECT_TRUE(group_->Initialize(mock_decoder_.get(), init.context_type,
            DisallowedFeatures()));

        if (init.context_type == CONTEXT_TYPE_WEBGL2 || init.context_type == CONTEXT_TYPE_OPENGLES3) {
            EXPECT_CALL(*gl_, GetIntegerv(GL_MAX_COLOR_ATTACHMENTS, _))
                .WillOnce(SetArgumentPointee<1>(kMaxColorAttachments))
                .RetiresOnSaturation();
            EXPECT_CALL(*gl_, GetIntegerv(GL_MAX_DRAW_BUFFERS, _))
                .WillOnce(SetArgumentPointee<1>(kMaxDrawBuffers))
                .RetiresOnSaturation();
        }

        if (group_->feature_info()->feature_flags().native_vertex_array_object) {
            EXPECT_CALL(*gl_, GenVertexArraysOES(1, _))
                .WillOnce(SetArgumentPointee<1>(kServiceVertexArrayId))
                .RetiresOnSaturation();
            EXPECT_CALL(*gl_, BindVertexArrayOES(_)).Times(1).RetiresOnSaturation();
        }

        if (group_->feature_info()->workarounds().init_vertex_attributes)
            AddExpectationsForVertexAttribManager();

        AddExpectationsForBindVertexArrayOES();

        EXPECT_CALL(*gl_, EnableVertexAttribArray(0))
            .Times(1)
            .RetiresOnSaturation();
        static GLuint attrib_0_id[] = {
            kServiceAttrib0BufferId,
        };
        static GLuint fixed_attrib_buffer_id[] = {
            kServiceFixedAttribBufferId,
        };
        EXPECT_CALL(*gl_, GenBuffersARB(arraysize(attrib_0_id), _))
            .WillOnce(SetArrayArgument<1>(attrib_0_id,
                attrib_0_id + arraysize(attrib_0_id)))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, kServiceAttrib0BufferId))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, VertexAttribPointer(0, 1, GL_FLOAT, GL_FALSE, 0, NULL))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, 0))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GenBuffersARB(arraysize(fixed_attrib_buffer_id), _))
            .WillOnce(SetArrayArgument<1>(
                fixed_attrib_buffer_id,
                fixed_attrib_buffer_id + arraysize(fixed_attrib_buffer_id)))
            .RetiresOnSaturation();

        for (GLint tt = 0; tt < TestHelper::kNumTextureUnits; ++tt) {
            EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0 + tt))
                .Times(1)
                .RetiresOnSaturation();
            if (group_->feature_info()->feature_flags().oes_egl_image_external) {
                EXPECT_CALL(*gl_,
                    BindTexture(GL_TEXTURE_EXTERNAL_OES,
                        use_default_textures
                            ? TestHelper::kServiceDefaultExternalTextureId
                            : 0))
                    .Times(1)
                    .RetiresOnSaturation();
            }
            if (group_->feature_info()->feature_flags().arb_texture_rectangle) {
                EXPECT_CALL(
                    *gl_,
                    BindTexture(GL_TEXTURE_RECTANGLE_ARB,
                        use_default_textures
                            ? TestHelper::kServiceDefaultRectangleTextureId
                            : 0))
                    .Times(1)
                    .RetiresOnSaturation();
            }
            EXPECT_CALL(*gl_,
                BindTexture(GL_TEXTURE_CUBE_MAP,
                    use_default_textures
                        ? TestHelper::kServiceDefaultTextureCubemapId
                        : 0))
                .Times(1)
                .RetiresOnSaturation();
            EXPECT_CALL(
                *gl_,
                BindTexture(
                    GL_TEXTURE_2D,
                    use_default_textures ? TestHelper::kServiceDefaultTexture2dId : 0))
                .Times(1)
                .RetiresOnSaturation();
        }
        EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
            .Times(1)
            .RetiresOnSaturation();

        EXPECT_CALL(*gl_, BindFramebufferEXT(GL_FRAMEBUFFER, 0))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetIntegerv(GL_ALPHA_BITS, _))
            .WillOnce(SetArgumentPointee<1>(normalized_init.has_alpha ? 8 : 0))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetIntegerv(GL_DEPTH_BITS, _))
            .WillOnce(SetArgumentPointee<1>(normalized_init.has_depth ? 24 : 0))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetIntegerv(GL_STENCIL_BITS, _))
            .WillOnce(SetArgumentPointee<1>(normalized_init.has_stencil ? 8 : 0))
            .RetiresOnSaturation();

        if (!group_->feature_info()->gl_version_info().BehavesLikeGLES()) {
            EXPECT_CALL(*gl_, Enable(GL_VERTEX_PROGRAM_POINT_SIZE))
                .Times(1)
                .RetiresOnSaturation();

            EXPECT_CALL(*gl_, Enable(GL_POINT_SPRITE))
                .Times(1)
                .RetiresOnSaturation();
        }

        static GLint max_viewport_dims[] = {
            kMaxViewportWidth,
            kMaxViewportHeight
        };
        EXPECT_CALL(*gl_, GetIntegerv(GL_MAX_VIEWPORT_DIMS, _))
            .WillOnce(SetArrayArgument<1>(
                max_viewport_dims, max_viewport_dims + arraysize(max_viewport_dims)))
            .RetiresOnSaturation();

        SetupInitCapabilitiesExpectations(group_->feature_info()->IsES3Capable());
        SetupInitStateExpectations();

        EXPECT_CALL(*gl_, ActiveTexture(GL_TEXTURE0))
            .Times(1)
            .RetiresOnSaturation();

        EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, 0))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, BindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, BindFramebufferEXT(GL_FRAMEBUFFER, 0))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, BindRenderbufferEXT(GL_RENDERBUFFER, 0))
            .Times(1)
            .RetiresOnSaturation();

        // TODO(boliu): Remove OS_ANDROID once crbug.com/259023 is fixed and the
        // workaround has been reverted.
#if !defined(OS_ANDROID)
        if (normalized_init.has_alpha && !normalized_init.request_alpha) {
            EXPECT_CALL(*gl_, ClearColor(0, 0, 0, 1)).Times(1).RetiresOnSaturation();
        }

        EXPECT_CALL(*gl_, Clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT))
            .Times(1)
            .RetiresOnSaturation();

        if (normalized_init.has_alpha && !normalized_init.request_alpha) {
            EXPECT_CALL(*gl_, ClearColor(0, 0, 0, 0)).Times(1).RetiresOnSaturation();
        }
#endif

        engine_.reset(new StrictMock<MockCommandBufferEngine>());
        scoped_refptr<gpu::Buffer> buffer = engine_->GetSharedMemoryBuffer(kSharedMemoryId);
        shared_memory_offset_ = kSharedMemoryOffset;
        shared_memory_address_ = reinterpret_cast<int8*>(buffer->memory()) + shared_memory_offset_;
        shared_memory_id_ = kSharedMemoryId;
        shared_memory_base_ = buffer->memory();

        static const int32 kLoseContextWhenOutOfMemory = 0x10002;
        static const int32 kWebGLVersion = 0x10003;

        int32 attributes[] = { EGL_ALPHA_SIZE,
            normalized_init.request_alpha ? 8 : 0,
            EGL_DEPTH_SIZE,
            normalized_init.request_depth ? 24 : 0,
            EGL_STENCIL_SIZE,
            normalized_init.request_stencil ? 8 : 0,
            kLoseContextWhenOutOfMemory,
            normalized_init.lose_context_when_out_of_memory ? 1 : 0,
            kWebGLVersion,
            init.context_type };
        std::vector<int32> attribs(attributes, attributes + arraysize(attributes));

        decoder_.reset(GLES2Decoder::Create(group_.get()));
        decoder_->SetIgnoreCachedStateForTest(ignore_cached_state_for_test_);
        decoder_->GetLogger()->set_log_synthesized_gl_errors(false);
        ASSERT_TRUE(decoder_->Initialize(surface_, context_, false,
            surface_->GetSize(), DisallowedFeatures(),
            attribs));

        EXPECT_CALL(*context_, MakeCurrent(surface_.get())).WillOnce(Return(true));
        if (context_->WasAllocatedUsingRobustnessExtension()) {
            EXPECT_CALL(*gl_, GetGraphicsResetStatusARB())
                .WillOnce(Return(GL_NO_ERROR));
        }
        decoder_->MakeCurrent();
        decoder_->set_engine(engine_.get());
        decoder_->BeginDecoding();

        EXPECT_CALL(*gl_, GenBuffersARB(_, _))
            .WillOnce(SetArgumentPointee<1>(kServiceBufferId))
            .RetiresOnSaturation();
        GenHelper<cmds::GenBuffersImmediate>(client_buffer_id_);
        EXPECT_CALL(*gl_, GenFramebuffersEXT(_, _))
            .WillOnce(SetArgumentPointee<1>(kServiceFramebufferId))
            .RetiresOnSaturation();
        GenHelper<cmds::GenFramebuffersImmediate>(client_framebuffer_id_);
        EXPECT_CALL(*gl_, GenRenderbuffersEXT(_, _))
            .WillOnce(SetArgumentPointee<1>(kServiceRenderbufferId))
            .RetiresOnSaturation();
        GenHelper<cmds::GenRenderbuffersImmediate>(client_renderbuffer_id_);
        EXPECT_CALL(*gl_, GenTextures(_, _))
            .WillOnce(SetArgumentPointee<1>(kServiceTextureId))
            .RetiresOnSaturation();
        GenHelper<cmds::GenTexturesImmediate>(client_texture_id_);
        EXPECT_CALL(*gl_, GenBuffersARB(_, _))
            .WillOnce(SetArgumentPointee<1>(kServiceElementBufferId))
            .RetiresOnSaturation();
        GenHelper<cmds::GenBuffersImmediate>(client_element_buffer_id_);

        DoCreateProgram(client_program_id_, kServiceProgramId);
        DoCreateShader(GL_VERTEX_SHADER, client_shader_id_, kServiceShaderId);

        // Unsafe commands.
        bool reset_unsafe_es3_apis_enabled = false;
        if (!decoder_->unsafe_es3_apis_enabled()) {
            decoder_->set_unsafe_es3_apis_enabled(true);
            reset_unsafe_es3_apis_enabled = true;
        }

        const gfx::GLVersionInfo* version = context_->GetVersionInfo();
        if (version->IsAtLeastGL(3, 3) || version->IsAtLeastGLES(3, 0)) {
            EXPECT_CALL(*gl_, GenSamplers(_, _))
                .WillOnce(SetArgumentPointee<1>(kServiceSamplerId))
                .RetiresOnSaturation();
            GenHelper<cmds::GenSamplersImmediate>(client_sampler_id_);
        }
        if (version->IsAtLeastGL(4, 0) || version->IsAtLeastGLES(3, 0)) {
            EXPECT_CALL(*gl_, GenTransformFeedbacks(_, _))
                .WillOnce(SetArgumentPointee<1>(kServiceTransformFeedbackId))
                .RetiresOnSaturation();
            GenHelper<cmds::GenTransformFeedbacksImmediate>(
                client_transformfeedback_id_);
        }

        if (init.extensions.find("GL_ARB_sync ") != std::string::npos || version->IsAtLeastGL(3, 2) || version->IsAtLeastGLES(3, 0)) {
            DoFenceSync(client_sync_id_, kServiceSyncId);
        }

        if (reset_unsafe_es3_apis_enabled) {
            decoder_->set_unsafe_es3_apis_enabled(false);
        }

        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    void GLES2DecoderTestBase::ResetDecoder()
    {
        if (!decoder_.get())
            return;
        // All Tests should have read all their GLErrors before getting here.
        EXPECT_EQ(GL_NO_ERROR, GetGLError());

        EXPECT_CALL(*gl_, DeleteBuffersARB(1, _))
            .Times(2)
            .RetiresOnSaturation();
        if (group_->feature_info()->feature_flags().native_vertex_array_object) {
            EXPECT_CALL(*gl_, DeleteVertexArraysOES(1, Pointee(kServiceVertexArrayId)))
                .Times(1)
                .RetiresOnSaturation();
        }

        decoder_->EndDecoding();
        decoder_->Destroy(true);
        decoder_.reset();
        group_->Destroy(mock_decoder_.get(), false);
        engine_.reset();
        ::gfx::MockGLInterface::SetGLInterface(NULL);
        gl_.reset();
        gfx::ClearGLBindings();
    }

    void GLES2DecoderTestBase::TearDown()
    {
        ResetDecoder();
    }

    void GLES2DecoderTestBase::ExpectEnableDisable(GLenum cap, bool enable)
    {
        if (enable) {
            EXPECT_CALL(*gl_, Enable(cap))
                .Times(1)
                .RetiresOnSaturation();
        } else {
            EXPECT_CALL(*gl_, Disable(cap))
                .Times(1)
                .RetiresOnSaturation();
        }
    }

    GLint GLES2DecoderTestBase::GetGLError()
    {
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        cmds::GetError cmd;
        cmd.Init(shared_memory_id_, shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        return static_cast<GLint>(*GetSharedMemoryAs<GLenum*>());
    }

    void GLES2DecoderTestBase::DoCreateShader(
        GLenum shader_type, GLuint client_id, GLuint service_id)
    {
        EXPECT_CALL(*gl_, CreateShader(shader_type))
            .Times(1)
            .WillOnce(Return(service_id))
            .RetiresOnSaturation();
        cmds::CreateShader cmd;
        cmd.Init(shader_type, client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    bool GLES2DecoderTestBase::DoIsShader(GLuint client_id)
    {
        return IsObjectHelper<cmds::IsShader, cmds::IsShader::Result>(client_id);
    }

    void GLES2DecoderTestBase::DoDeleteShader(
        GLuint client_id, GLuint service_id)
    {
        EXPECT_CALL(*gl_, DeleteShader(service_id))
            .Times(1)
            .RetiresOnSaturation();
        cmds::DeleteShader cmd;
        cmd.Init(client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoCreateProgram(
        GLuint client_id, GLuint service_id)
    {
        EXPECT_CALL(*gl_, CreateProgram())
            .Times(1)
            .WillOnce(Return(service_id))
            .RetiresOnSaturation();
        cmds::CreateProgram cmd;
        cmd.Init(client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    bool GLES2DecoderTestBase::DoIsProgram(GLuint client_id)
    {
        return IsObjectHelper<cmds::IsProgram, cmds::IsProgram::Result>(client_id);
    }

    void GLES2DecoderTestBase::DoDeleteProgram(
        GLuint client_id, GLuint /* service_id */)
    {
        cmds::DeleteProgram cmd;
        cmd.Init(client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoFenceSync(
        GLuint client_id, GLuint service_id)
    {
        EXPECT_CALL(*gl_, FenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0))
            .Times(1)
            .WillOnce(Return(reinterpret_cast<GLsync>(service_id)))
            .RetiresOnSaturation();
        cmds::FenceSync cmd;
        cmd.Init(client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::SetBucketData(
        uint32_t bucket_id, const void* data, uint32_t data_size)
    {
        DCHECK(data || data_size == 0);
        cmd::SetBucketSize cmd1;
        cmd1.Init(bucket_id, data_size);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd1));
        if (data) {
            memcpy(shared_memory_address_, data, data_size);
            cmd::SetBucketData cmd2;
            cmd2.Init(bucket_id, 0, data_size, kSharedMemoryId, kSharedMemoryOffset);
            EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
            ClearSharedMemory();
        }
    }

    void GLES2DecoderTestBase::SetBucketAsCString(
        uint32 bucket_id, const char* str)
    {
        SetBucketData(bucket_id, str, str ? (strlen(str) + 1) : 0);
    }

    void GLES2DecoderTestBase::SetBucketAsCStrings(
        uint32 bucket_id, GLsizei count, const char** str,
        GLsizei count_in_header, char str_end)
    {
        uint32_t header_size = sizeof(GLint) * (count + 1);
        uint32_t total_size = header_size;
        scoped_ptr<GLint[]> header(new GLint[count + 1]);
        header[0] = static_cast<GLint>(count_in_header);
        for (GLsizei ii = 0; ii < count; ++ii) {
            header[ii + 1] = str && str[ii] ? strlen(str[ii]) : 0;
            total_size += header[ii + 1] + 1;
        }
        cmd::SetBucketSize cmd1;
        cmd1.Init(bucket_id, total_size);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd1));
        memcpy(shared_memory_address_, header.get(), header_size);
        uint32_t offset = header_size;
        for (GLsizei ii = 0; ii < count; ++ii) {
            if (str && str[ii]) {
                size_t str_len = strlen(str[ii]);
                memcpy(reinterpret_cast<char*>(shared_memory_address_) + offset,
                    str[ii], str_len);
                offset += str_len;
            }
            memcpy(reinterpret_cast<char*>(shared_memory_address_) + offset,
                &str_end, 1);
            offset += 1;
        }
        cmd::SetBucketData cmd2;
        cmd2.Init(bucket_id, 0, total_size, kSharedMemoryId, kSharedMemoryOffset);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd2));
        ClearSharedMemory();
    }

    void GLES2DecoderTestBase::SetupClearTextureExpectations(GLuint service_id,
        GLuint old_service_id,
        GLenum bind_target,
        GLenum target,
        GLint level,
        GLenum internal_format,
        GLenum format,
        GLenum type,
        GLint xoffset,
        GLint yoffset,
        GLsizei width,
        GLsizei height)
    {
        EXPECT_CALL(*gl_, BindTexture(bind_target, service_id))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, TexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, _))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, BindTexture(bind_target, old_service_id))
            .Times(1)
            .RetiresOnSaturation();
    }

    void GLES2DecoderTestBase::SetupExpectationsForFramebufferClearing(
        GLenum target,
        GLuint clear_bits,
        GLclampf restore_red,
        GLclampf restore_green,
        GLclampf restore_blue,
        GLclampf restore_alpha,
        GLuint restore_stencil,
        GLclampf restore_depth,
        bool restore_scissor_test,
        GLint restore_scissor_x,
        GLint restore_scissor_y,
        GLsizei restore_scissor_width,
        GLsizei restore_scissor_height)
    {
        SetupExpectationsForFramebufferClearingMulti(
            0, 0, target, clear_bits, restore_red, restore_green, restore_blue,
            restore_alpha, restore_stencil, restore_depth, restore_scissor_test,
            restore_scissor_x, restore_scissor_y, restore_scissor_width,
            restore_scissor_height);
    }

    void GLES2DecoderTestBase::SetupExpectationsForRestoreClearState(
        GLclampf restore_red,
        GLclampf restore_green,
        GLclampf restore_blue,
        GLclampf restore_alpha,
        GLuint restore_stencil,
        GLclampf restore_depth,
        bool restore_scissor_test,
        GLint restore_scissor_x,
        GLint restore_scissor_y,
        GLsizei restore_scissor_width,
        GLsizei restore_scissor_height)
    {
        EXPECT_CALL(*gl_, ClearColor(restore_red, restore_green, restore_blue, restore_alpha))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, ClearStencil(restore_stencil))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, ClearDepth(restore_depth))
            .Times(1)
            .RetiresOnSaturation();
        SetupExpectationsForEnableDisable(GL_SCISSOR_TEST, restore_scissor_test);
        EXPECT_CALL(*gl_, Scissor(restore_scissor_x, restore_scissor_y, restore_scissor_width, restore_scissor_height))
            .Times(1)
            .RetiresOnSaturation();
    }

    void GLES2DecoderTestBase::SetupExpectationsForFramebufferClearingMulti(
        GLuint read_framebuffer_service_id,
        GLuint draw_framebuffer_service_id,
        GLenum target,
        GLuint clear_bits,
        GLclampf restore_red,
        GLclampf restore_green,
        GLclampf restore_blue,
        GLclampf restore_alpha,
        GLuint restore_stencil,
        GLclampf restore_depth,
        bool restore_scissor_test,
        GLint restore_scissor_x,
        GLint restore_scissor_y,
        GLsizei restore_scissor_width,
        GLsizei restore_scissor_height)
    {
        // TODO(gman): Figure out why InSequence stopped working.
        // InSequence sequence;
        EXPECT_CALL(*gl_, CheckFramebufferStatusEXT(target))
            .WillOnce(Return(GL_FRAMEBUFFER_COMPLETE))
            .RetiresOnSaturation();
        if (target == GL_READ_FRAMEBUFFER_EXT) {
            EXPECT_CALL(*gl_, BindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0))
                .Times(1)
                .RetiresOnSaturation();
            EXPECT_CALL(*gl_, BindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, read_framebuffer_service_id))
                .Times(1)
                .RetiresOnSaturation();
        }
        if ((clear_bits & GL_COLOR_BUFFER_BIT) != 0) {
            EXPECT_CALL(*gl_, ClearColor(0.0f, 0.0f, 0.0f, 0.0f))
                .Times(1)
                .RetiresOnSaturation();
            SetupExpectationsForColorMask(true, true, true, true);
        }
        if ((clear_bits & GL_STENCIL_BUFFER_BIT) != 0) {
            EXPECT_CALL(*gl_, ClearStencil(0))
                .Times(1)
                .RetiresOnSaturation();
            EXPECT_CALL(*gl_, StencilMask(static_cast<GLuint>(-1)))
                .Times(1)
                .RetiresOnSaturation();
        }
        if ((clear_bits & GL_DEPTH_BUFFER_BIT) != 0) {
            EXPECT_CALL(*gl_, ClearDepth(1.0f))
                .Times(1)
                .RetiresOnSaturation();
            SetupExpectationsForDepthMask(true);
        }
        SetupExpectationsForEnableDisable(GL_SCISSOR_TEST, false);
        EXPECT_CALL(*gl_, Clear(clear_bits))
            .Times(1)
            .RetiresOnSaturation();
        SetupExpectationsForRestoreClearState(
            restore_red, restore_green, restore_blue, restore_alpha, restore_stencil,
            restore_depth, restore_scissor_test, restore_scissor_x, restore_scissor_y,
            restore_scissor_width, restore_scissor_height);
        if (target == GL_READ_FRAMEBUFFER_EXT) {
            EXPECT_CALL(*gl_, BindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, read_framebuffer_service_id))
                .Times(1)
                .RetiresOnSaturation();
            EXPECT_CALL(*gl_, BindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, draw_framebuffer_service_id))
                .Times(1)
                .RetiresOnSaturation();
        }
    }

    void GLES2DecoderTestBase::SetupShaderForUniform(GLenum uniform_type)
    {
        static AttribInfo attribs[] = {
            {
                "foo",
                1,
                GL_FLOAT,
                1,
            },
            {
                "goo",
                1,
                GL_FLOAT,
                2,
            },
        };
        UniformInfo uniforms[] = {
            {
                "bar",
                1,
                uniform_type,
                0,
                2,
                -1,
            },
            {
                "car",
                4,
                uniform_type,
                1,
                1,
                -1,
            },
        };
        const GLuint kClientVertexShaderId = 5001;
        const GLuint kServiceVertexShaderId = 6001;
        const GLuint kClientFragmentShaderId = 5002;
        const GLuint kServiceFragmentShaderId = 6002;
        SetupShader(attribs, arraysize(attribs), uniforms, arraysize(uniforms),
            client_program_id_, kServiceProgramId,
            kClientVertexShaderId, kServiceVertexShaderId,
            kClientFragmentShaderId, kServiceFragmentShaderId);

        EXPECT_CALL(*gl_, UseProgram(kServiceProgramId))
            .Times(1)
            .RetiresOnSaturation();
        cmds::UseProgram cmd;
        cmd.Init(client_program_id_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoBindBuffer(
        GLenum target, GLuint client_id, GLuint service_id)
    {
        EXPECT_CALL(*gl_, BindBuffer(target, service_id))
            .Times(1)
            .RetiresOnSaturation();
        cmds::BindBuffer cmd;
        cmd.Init(target, client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    bool GLES2DecoderTestBase::DoIsBuffer(GLuint client_id)
    {
        return IsObjectHelper<cmds::IsBuffer, cmds::IsBuffer::Result>(client_id);
    }

    void GLES2DecoderTestBase::DoDeleteBuffer(
        GLuint client_id, GLuint service_id)
    {
        EXPECT_CALL(*gl_, DeleteBuffersARB(1, Pointee(service_id)))
            .Times(1)
            .RetiresOnSaturation();
        GenHelper<cmds::DeleteBuffersImmediate>(client_id);
    }

    void GLES2DecoderTestBase::SetupExpectationsForColorMask(bool red,
        bool green,
        bool blue,
        bool alpha)
    {
        if (ignore_cached_state_for_test_ || cached_color_mask_red_ != red || cached_color_mask_green_ != green || cached_color_mask_blue_ != blue || cached_color_mask_alpha_ != alpha) {
            cached_color_mask_red_ = red;
            cached_color_mask_green_ = green;
            cached_color_mask_blue_ = blue;
            cached_color_mask_alpha_ = alpha;
            EXPECT_CALL(*gl_, ColorMask(red, green, blue, alpha))
                .Times(1)
                .RetiresOnSaturation();
        }
    }

    void GLES2DecoderTestBase::SetupExpectationsForDepthMask(bool mask)
    {
        if (ignore_cached_state_for_test_ || cached_depth_mask_ != mask) {
            cached_depth_mask_ = mask;
            EXPECT_CALL(*gl_, DepthMask(mask)).Times(1).RetiresOnSaturation();
        }
    }

    void GLES2DecoderTestBase::SetupExpectationsForStencilMask(GLuint front_mask,
        GLuint back_mask)
    {
        if (ignore_cached_state_for_test_ || cached_stencil_front_mask_ != front_mask) {
            cached_stencil_front_mask_ = front_mask;
            EXPECT_CALL(*gl_, StencilMaskSeparate(GL_FRONT, front_mask))
                .Times(1)
                .RetiresOnSaturation();
        }

        if (ignore_cached_state_for_test_ || cached_stencil_back_mask_ != back_mask) {
            cached_stencil_back_mask_ = back_mask;
            EXPECT_CALL(*gl_, StencilMaskSeparate(GL_BACK, back_mask))
                .Times(1)
                .RetiresOnSaturation();
        }
    }

    void GLES2DecoderTestBase::SetupExpectationsForEnableDisable(GLenum cap,
        bool enable)
    {
        switch (cap) {
        case GL_BLEND:
            if (enable_flags_.cached_blend == enable && !ignore_cached_state_for_test_)
                return;
            enable_flags_.cached_blend = enable;
            break;
        case GL_CULL_FACE:
            if (enable_flags_.cached_cull_face == enable && !ignore_cached_state_for_test_)
                return;
            enable_flags_.cached_cull_face = enable;
            break;
        case GL_DEPTH_TEST:
            if (enable_flags_.cached_depth_test == enable && !ignore_cached_state_for_test_)
                return;
            enable_flags_.cached_depth_test = enable;
            break;
        case GL_DITHER:
            if (enable_flags_.cached_dither == enable && !ignore_cached_state_for_test_)
                return;
            enable_flags_.cached_dither = enable;
            break;
        case GL_POLYGON_OFFSET_FILL:
            if (enable_flags_.cached_polygon_offset_fill == enable && !ignore_cached_state_for_test_)
                return;
            enable_flags_.cached_polygon_offset_fill = enable;
            break;
        case GL_SAMPLE_ALPHA_TO_COVERAGE:
            if (enable_flags_.cached_sample_alpha_to_coverage == enable && !ignore_cached_state_for_test_)
                return;
            enable_flags_.cached_sample_alpha_to_coverage = enable;
            break;
        case GL_SAMPLE_COVERAGE:
            if (enable_flags_.cached_sample_coverage == enable && !ignore_cached_state_for_test_)
                return;
            enable_flags_.cached_sample_coverage = enable;
            break;
        case GL_SCISSOR_TEST:
            if (enable_flags_.cached_scissor_test == enable && !ignore_cached_state_for_test_)
                return;
            enable_flags_.cached_scissor_test = enable;
            break;
        case GL_STENCIL_TEST:
            if (enable_flags_.cached_stencil_test == enable && !ignore_cached_state_for_test_)
                return;
            enable_flags_.cached_stencil_test = enable;
            break;
        default:
            NOTREACHED();
            return;
        }
        if (enable) {
            EXPECT_CALL(*gl_, Enable(cap)).Times(1).RetiresOnSaturation();
        } else {
            EXPECT_CALL(*gl_, Disable(cap)).Times(1).RetiresOnSaturation();
        }
    }

    void GLES2DecoderTestBase::SetupExpectationsForApplyingDirtyState(
        bool framebuffer_is_rgb,
        bool framebuffer_has_depth,
        bool framebuffer_has_stencil,
        GLuint color_bits,
        bool depth_mask,
        bool depth_enabled,
        GLuint front_stencil_mask,
        GLuint back_stencil_mask,
        bool stencil_enabled)
    {
        bool color_mask_red = (color_bits & 0x1000) != 0;
        bool color_mask_green = (color_bits & 0x0100) != 0;
        bool color_mask_blue = (color_bits & 0x0010) != 0;
        bool color_mask_alpha = (color_bits & 0x0001) && !framebuffer_is_rgb;

        SetupExpectationsForColorMask(
            color_mask_red, color_mask_green, color_mask_blue, color_mask_alpha);
        SetupExpectationsForDepthMask(depth_mask);
        SetupExpectationsForStencilMask(front_stencil_mask, back_stencil_mask);
        SetupExpectationsForEnableDisable(GL_DEPTH_TEST,
            framebuffer_has_depth && depth_enabled);
        SetupExpectationsForEnableDisable(GL_STENCIL_TEST,
            framebuffer_has_stencil && stencil_enabled);
    }

    void GLES2DecoderTestBase::SetupExpectationsForApplyingDefaultDirtyState()
    {
        SetupExpectationsForApplyingDirtyState(false, // Framebuffer is RGB
            false, // Framebuffer has depth
            false, // Framebuffer has stencil
            0x1111, // color bits
            true, // depth mask
            false, // depth enabled
            0, // front stencil mask
            0, // back stencil mask
            false); // stencil enabled
    }

    GLES2DecoderTestBase::EnableFlags::EnableFlags()
        : cached_blend(false)
        , cached_cull_face(false)
        , cached_depth_test(false)
        , cached_dither(true)
        , cached_polygon_offset_fill(false)
        , cached_sample_alpha_to_coverage(false)
        , cached_sample_coverage(false)
        , cached_scissor_test(false)
        , cached_stencil_test(false)
    {
    }

    void GLES2DecoderTestBase::DoBindFramebuffer(
        GLenum target, GLuint client_id, GLuint service_id)
    {
        EXPECT_CALL(*gl_, BindFramebufferEXT(target, service_id))
            .Times(1)
            .RetiresOnSaturation();
        cmds::BindFramebuffer cmd;
        cmd.Init(target, client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    bool GLES2DecoderTestBase::DoIsFramebuffer(GLuint client_id)
    {
        return IsObjectHelper<cmds::IsFramebuffer, cmds::IsFramebuffer::Result>(
            client_id);
    }

    void GLES2DecoderTestBase::DoDeleteFramebuffer(
        GLuint client_id, GLuint service_id,
        bool reset_draw, GLenum draw_target, GLuint draw_id,
        bool reset_read, GLenum read_target, GLuint read_id)
    {
        if (reset_draw) {
            EXPECT_CALL(*gl_, BindFramebufferEXT(draw_target, draw_id))
                .Times(1)
                .RetiresOnSaturation();
        }
        if (reset_read) {
            EXPECT_CALL(*gl_, BindFramebufferEXT(read_target, read_id))
                .Times(1)
                .RetiresOnSaturation();
        }
        EXPECT_CALL(*gl_, DeleteFramebuffersEXT(1, Pointee(service_id)))
            .Times(1)
            .RetiresOnSaturation();
        GenHelper<cmds::DeleteFramebuffersImmediate>(client_id);
    }

    void GLES2DecoderTestBase::DoBindRenderbuffer(
        GLenum target, GLuint client_id, GLuint service_id)
    {
        service_renderbuffer_id_ = service_id;
        service_renderbuffer_valid_ = true;
        EXPECT_CALL(*gl_, BindRenderbufferEXT(target, service_id))
            .Times(1)
            .RetiresOnSaturation();
        cmds::BindRenderbuffer cmd;
        cmd.Init(target, client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoRenderbufferStorageMultisampleCHROMIUM(
        GLenum target,
        GLsizei samples,
        GLenum internal_format,
        GLenum gl_format,
        GLsizei width,
        GLsizei height)
    {
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_,
            RenderbufferStorageMultisampleEXT(
                target, samples, gl_format, width, height))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        cmds::RenderbufferStorageMultisampleCHROMIUM cmd;
        cmd.Init(target, samples, internal_format, width, height);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    void GLES2DecoderTestBase::RestoreRenderbufferBindings()
    {
        GetDecoder()->RestoreRenderbufferBindings();
        service_renderbuffer_valid_ = false;
    }

    void GLES2DecoderTestBase::EnsureRenderbufferBound(bool expect_bind)
    {
        EXPECT_NE(expect_bind, service_renderbuffer_valid_);

        if (expect_bind) {
            service_renderbuffer_valid_ = true;
            EXPECT_CALL(*gl_,
                BindRenderbufferEXT(GL_RENDERBUFFER, service_renderbuffer_id_))
                .Times(1)
                .RetiresOnSaturation();
        } else {
            EXPECT_CALL(*gl_, BindRenderbufferEXT(_, _)).Times(0);
        }
    }

    bool GLES2DecoderTestBase::DoIsRenderbuffer(GLuint client_id)
    {
        return IsObjectHelper<cmds::IsRenderbuffer, cmds::IsRenderbuffer::Result>(
            client_id);
    }

    void GLES2DecoderTestBase::DoDeleteRenderbuffer(
        GLuint client_id, GLuint service_id)
    {
        EXPECT_CALL(*gl_, DeleteRenderbuffersEXT(1, Pointee(service_id)))
            .Times(1)
            .RetiresOnSaturation();
        GenHelper<cmds::DeleteRenderbuffersImmediate>(client_id);
    }

    void GLES2DecoderTestBase::DoBindTexture(
        GLenum target, GLuint client_id, GLuint service_id)
    {
        EXPECT_CALL(*gl_, BindTexture(target, service_id))
            .Times(1)
            .RetiresOnSaturation();
        cmds::BindTexture cmd;
        cmd.Init(target, client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    bool GLES2DecoderTestBase::DoIsTexture(GLuint client_id)
    {
        return IsObjectHelper<cmds::IsTexture, cmds::IsTexture::Result>(client_id);
    }

    void GLES2DecoderTestBase::DoDeleteTexture(
        GLuint client_id, GLuint service_id)
    {

        {
            InSequence s;

            // Calling DoDeleteTexture will unbind the texture from any texture units
            // it's currently bound to.
            EXPECT_CALL(*gl_, BindTexture(_, 0))
                .Times(AnyNumber());

            EXPECT_CALL(*gl_, DeleteTextures(1, Pointee(service_id)))
                .Times(1)
                .RetiresOnSaturation();

            GenHelper<cmds::DeleteTexturesImmediate>(client_id);
        }
    }

    void GLES2DecoderTestBase::DoBindTexImage2DCHROMIUM(GLenum target,
        GLint image_id)
    {
        cmds::BindTexImage2DCHROMIUM bind_tex_image_2d_cmd;
        bind_tex_image_2d_cmd.Init(target, image_id);
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_EQ(error::kNoError, ExecuteCmd(bind_tex_image_2d_cmd));
        EXPECT_EQ(GL_NO_ERROR, GetGLError());
    }

    void GLES2DecoderTestBase::DoTexImage2D(
        GLenum target, GLint level, GLenum internal_format,
        GLsizei width, GLsizei height, GLint border,
        GLenum format, GLenum type,
        uint32 shared_memory_id, uint32 shared_memory_offset)
    {
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, TexImage2D(target, level, internal_format, width, height, border, format, type, _))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        cmds::TexImage2D cmd;
        cmd.Init(target, level, internal_format, width, height, format,
            type, shared_memory_id, shared_memory_offset);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoTexImage2DConvertInternalFormat(
        GLenum target, GLint level, GLenum requested_internal_format,
        GLsizei width, GLsizei height, GLint border,
        GLenum format, GLenum type,
        uint32 shared_memory_id, uint32 shared_memory_offset,
        GLenum expected_internal_format)
    {
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, TexImage2D(target, level, expected_internal_format, width, height, border, format, type, _))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        cmds::TexImage2D cmd;
        cmd.Init(target, level, requested_internal_format, width, height,
            format, type, shared_memory_id, shared_memory_offset);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoCompressedTexImage2D(
        GLenum target, GLint level, GLenum format,
        GLsizei width, GLsizei height, GLint border,
        GLsizei size, uint32 bucket_id)
    {
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, CompressedTexImage2D(target, level, format, width, height, border, size, _))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        CommonDecoder::Bucket* bucket = decoder_->CreateBucket(bucket_id);
        bucket->SetSize(size);
        cmds::CompressedTexImage2DBucket cmd;
        cmd.Init(
            target, level, format, width, height,
            bucket_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoRenderbufferStorage(
        GLenum target, GLenum internal_format, GLenum actual_format,
        GLsizei width, GLsizei height, GLenum error)
    {
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, RenderbufferStorageEXT(target, actual_format, width, height))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(error))
            .RetiresOnSaturation();
        cmds::RenderbufferStorage cmd;
        cmd.Init(target, internal_format, width, height);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoFramebufferTexture2D(
        GLenum target, GLenum attachment, GLenum textarget,
        GLuint texture_client_id, GLuint texture_service_id, GLint level,
        GLenum error)
    {
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, FramebufferTexture2DEXT(target, attachment, textarget, texture_service_id, level))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(error))
            .RetiresOnSaturation();
        cmds::FramebufferTexture2D cmd;
        cmd.Init(target, attachment, textarget, texture_client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoFramebufferRenderbuffer(
        GLenum target,
        GLenum attachment,
        GLenum renderbuffer_target,
        GLuint renderbuffer_client_id,
        GLuint renderbuffer_service_id,
        GLenum error)
    {
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, FramebufferRenderbufferEXT(target, attachment, renderbuffer_target, renderbuffer_service_id))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(error))
            .RetiresOnSaturation();
        cmds::FramebufferRenderbuffer cmd;
        cmd.Init(target, attachment, renderbuffer_target, renderbuffer_client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoVertexAttribPointer(
        GLuint index, GLint size, GLenum type, GLsizei stride, GLuint offset)
    {
        EXPECT_CALL(*gl_,
            VertexAttribPointer(index, size, type, GL_FALSE, stride,
                BufferOffset(offset)))
            .Times(1)
            .RetiresOnSaturation();
        cmds::VertexAttribPointer cmd;
        cmd.Init(index, size, GL_FLOAT, GL_FALSE, stride, offset);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoVertexAttribDivisorANGLE(
        GLuint index, GLuint divisor)
    {
        EXPECT_CALL(*gl_,
            VertexAttribDivisorANGLE(index, divisor))
            .Times(1)
            .RetiresOnSaturation();
        cmds::VertexAttribDivisorANGLE cmd;
        cmd.Init(index, divisor);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::AddExpectationsForGenVertexArraysOES()
    {
        if (group_->feature_info()->feature_flags().native_vertex_array_object) {
            EXPECT_CALL(*gl_, GenVertexArraysOES(1, _))
                .WillOnce(SetArgumentPointee<1>(kServiceVertexArrayId))
                .RetiresOnSaturation();
        }
    }

    void GLES2DecoderTestBase::AddExpectationsForDeleteVertexArraysOES()
    {
        if (group_->feature_info()->feature_flags().native_vertex_array_object) {
            EXPECT_CALL(*gl_, DeleteVertexArraysOES(1, _))
                .Times(1)
                .RetiresOnSaturation();
        }
    }

    void GLES2DecoderTestBase::AddExpectationsForDeleteBoundVertexArraysOES()
    {
        // Expectations are the same as a delete, followed by binding VAO 0.
        AddExpectationsForDeleteVertexArraysOES();
        AddExpectationsForBindVertexArrayOES();
    }

    void GLES2DecoderTestBase::AddExpectationsForBindVertexArrayOES()
    {
        if (group_->feature_info()->feature_flags().native_vertex_array_object) {
            EXPECT_CALL(*gl_, BindVertexArrayOES(_))
                .Times(1)
                .RetiresOnSaturation();
        } else {
            for (uint32 vv = 0; vv < group_->max_vertex_attribs(); ++vv) {
                AddExpectationsForRestoreAttribState(vv);
            }

            EXPECT_CALL(*gl_, BindBuffer(GL_ELEMENT_ARRAY_BUFFER, _))
                .Times(1)
                .RetiresOnSaturation();
        }
    }

    void GLES2DecoderTestBase::AddExpectationsForRestoreAttribState(GLuint attrib)
    {
        EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, _))
            .Times(1)
            .RetiresOnSaturation();

        EXPECT_CALL(*gl_, VertexAttribPointer(attrib, _, _, _, _, _))
            .Times(1)
            .RetiresOnSaturation();

        EXPECT_CALL(*gl_, VertexAttribDivisorANGLE(attrib, _))
            .Times(testing::AtMost(1))
            .RetiresOnSaturation();

        EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, _))
            .Times(1)
            .RetiresOnSaturation();

        if (attrib != 0 || gfx::GetGLImplementation() == gfx::kGLImplementationEGLGLES2) {

            // TODO(bajones): Not sure if I can tell which of these will be called
            EXPECT_CALL(*gl_, EnableVertexAttribArray(attrib))
                .Times(testing::AtMost(1))
                .RetiresOnSaturation();

            EXPECT_CALL(*gl_, DisableVertexAttribArray(attrib))
                .Times(testing::AtMost(1))
                .RetiresOnSaturation();
        }
    }

// GCC requires these declarations, but MSVC requires they not be present
#ifndef COMPILER_MSVC
    const int GLES2DecoderTestBase::kBackBufferWidth;
    const int GLES2DecoderTestBase::kBackBufferHeight;

    const GLint GLES2DecoderTestBase::kMaxTextureSize;
    const GLint GLES2DecoderTestBase::kMaxCubeMapTextureSize;
    const GLint GLES2DecoderTestBase::kNumVertexAttribs;
    const GLint GLES2DecoderTestBase::kNumTextureUnits;
    const GLint GLES2DecoderTestBase::kMaxTextureImageUnits;
    const GLint GLES2DecoderTestBase::kMaxVertexTextureImageUnits;
    const GLint GLES2DecoderTestBase::kMaxFragmentUniformVectors;
    const GLint GLES2DecoderTestBase::kMaxVaryingVectors;
    const GLint GLES2DecoderTestBase::kMaxVertexUniformVectors;
    const GLint GLES2DecoderTestBase::kMaxViewportWidth;
    const GLint GLES2DecoderTestBase::kMaxViewportHeight;

    const GLint GLES2DecoderTestBase::kViewportX;
    const GLint GLES2DecoderTestBase::kViewportY;
    const GLint GLES2DecoderTestBase::kViewportWidth;
    const GLint GLES2DecoderTestBase::kViewportHeight;

    const GLuint GLES2DecoderTestBase::kServiceAttrib0BufferId;
    const GLuint GLES2DecoderTestBase::kServiceFixedAttribBufferId;

    const GLuint GLES2DecoderTestBase::kServiceBufferId;
    const GLuint GLES2DecoderTestBase::kServiceFramebufferId;
    const GLuint GLES2DecoderTestBase::kServiceRenderbufferId;
    const GLuint GLES2DecoderTestBase::kServiceSamplerId;
    const GLuint GLES2DecoderTestBase::kServiceTextureId;
    const GLuint GLES2DecoderTestBase::kServiceProgramId;
    const GLuint GLES2DecoderTestBase::kServiceShaderId;
    const GLuint GLES2DecoderTestBase::kServiceElementBufferId;
    const GLuint GLES2DecoderTestBase::kServiceQueryId;
    const GLuint GLES2DecoderTestBase::kServiceVertexArrayId;
    const GLuint GLES2DecoderTestBase::kServiceTransformFeedbackId;
    const GLuint GLES2DecoderTestBase::kServiceSyncId;

    const int32 GLES2DecoderTestBase::kSharedMemoryId;
    const size_t GLES2DecoderTestBase::kSharedBufferSize;
    const uint32 GLES2DecoderTestBase::kSharedMemoryOffset;
    const int32 GLES2DecoderTestBase::kInvalidSharedMemoryId;
    const uint32 GLES2DecoderTestBase::kInvalidSharedMemoryOffset;
    const uint32 GLES2DecoderTestBase::kInitialResult;
    const uint8 GLES2DecoderTestBase::kInitialMemoryValue;

    const uint32 GLES2DecoderTestBase::kNewClientId;
    const uint32 GLES2DecoderTestBase::kNewServiceId;
    const uint32 GLES2DecoderTestBase::kInvalidClientId;

    const GLuint GLES2DecoderTestBase::kServiceVertexShaderId;
    const GLuint GLES2DecoderTestBase::kServiceFragmentShaderId;

    const GLuint GLES2DecoderTestBase::kServiceCopyTextureChromiumShaderId;
    const GLuint GLES2DecoderTestBase::kServiceCopyTextureChromiumProgramId;

    const GLuint GLES2DecoderTestBase::kServiceCopyTextureChromiumTextureBufferId;
    const GLuint GLES2DecoderTestBase::kServiceCopyTextureChromiumVertexBufferId;
    const GLuint GLES2DecoderTestBase::kServiceCopyTextureChromiumFBOId;
    const GLuint GLES2DecoderTestBase::kServiceCopyTextureChromiumPositionAttrib;
    const GLuint GLES2DecoderTestBase::kServiceCopyTextureChromiumTexAttrib;
    const GLuint GLES2DecoderTestBase::kServiceCopyTextureChromiumSamplerLocation;

    const GLsizei GLES2DecoderTestBase::kNumVertices;
    const GLsizei GLES2DecoderTestBase::kNumIndices;
    const int GLES2DecoderTestBase::kValidIndexRangeStart;
    const int GLES2DecoderTestBase::kValidIndexRangeCount;
    const int GLES2DecoderTestBase::kInvalidIndexRangeStart;
    const int GLES2DecoderTestBase::kInvalidIndexRangeCount;
    const int GLES2DecoderTestBase::kOutOfRangeIndexRangeEnd;
    const GLuint GLES2DecoderTestBase::kMaxValidIndex;

    const GLint GLES2DecoderTestBase::kMaxAttribLength;
    const GLint GLES2DecoderTestBase::kAttrib1Size;
    const GLint GLES2DecoderTestBase::kAttrib2Size;
    const GLint GLES2DecoderTestBase::kAttrib3Size;
    const GLint GLES2DecoderTestBase::kAttrib1Location;
    const GLint GLES2DecoderTestBase::kAttrib2Location;
    const GLint GLES2DecoderTestBase::kAttrib3Location;
    const GLenum GLES2DecoderTestBase::kAttrib1Type;
    const GLenum GLES2DecoderTestBase::kAttrib2Type;
    const GLenum GLES2DecoderTestBase::kAttrib3Type;
    const GLint GLES2DecoderTestBase::kInvalidAttribLocation;
    const GLint GLES2DecoderTestBase::kBadAttribIndex;

    const GLint GLES2DecoderTestBase::kMaxUniformLength;
    const GLint GLES2DecoderTestBase::kUniform1Size;
    const GLint GLES2DecoderTestBase::kUniform2Size;
    const GLint GLES2DecoderTestBase::kUniform3Size;
    const GLint GLES2DecoderTestBase::kUniform4Size;
    const GLint GLES2DecoderTestBase::kUniform5Size;
    const GLint GLES2DecoderTestBase::kUniform6Size;
    const GLint GLES2DecoderTestBase::kUniform7Size;
    const GLint GLES2DecoderTestBase::kUniform1RealLocation;
    const GLint GLES2DecoderTestBase::kUniform2RealLocation;
    const GLint GLES2DecoderTestBase::kUniform2ElementRealLocation;
    const GLint GLES2DecoderTestBase::kUniform3RealLocation;
    const GLint GLES2DecoderTestBase::kUniform4RealLocation;
    const GLint GLES2DecoderTestBase::kUniform5RealLocation;
    const GLint GLES2DecoderTestBase::kUniform6RealLocation;
    const GLint GLES2DecoderTestBase::kUniform7RealLocation;
    const GLint GLES2DecoderTestBase::kUniform1FakeLocation;
    const GLint GLES2DecoderTestBase::kUniform2FakeLocation;
    const GLint GLES2DecoderTestBase::kUniform2ElementFakeLocation;
    const GLint GLES2DecoderTestBase::kUniform3FakeLocation;
    const GLint GLES2DecoderTestBase::kUniform4FakeLocation;
    const GLint GLES2DecoderTestBase::kUniform5FakeLocation;
    const GLint GLES2DecoderTestBase::kUniform6FakeLocation;
    const GLint GLES2DecoderTestBase::kUniform7FakeLocation;
    const GLint GLES2DecoderTestBase::kUniform1DesiredLocation;
    const GLint GLES2DecoderTestBase::kUniform2DesiredLocation;
    const GLint GLES2DecoderTestBase::kUniform3DesiredLocation;
    const GLint GLES2DecoderTestBase::kUniform4DesiredLocation;
    const GLint GLES2DecoderTestBase::kUniform5DesiredLocation;
    const GLint GLES2DecoderTestBase::kUniform6DesiredLocation;
    const GLint GLES2DecoderTestBase::kUniform7DesiredLocation;
    const GLenum GLES2DecoderTestBase::kUniform1Type;
    const GLenum GLES2DecoderTestBase::kUniform2Type;
    const GLenum GLES2DecoderTestBase::kUniform3Type;
    const GLenum GLES2DecoderTestBase::kUniform4Type;
    const GLenum GLES2DecoderTestBase::kUniform5Type;
    const GLenum GLES2DecoderTestBase::kUniform6Type;
    const GLenum GLES2DecoderTestBase::kUniform7Type;
    const GLenum GLES2DecoderTestBase::kUniformCubemapType;
    const GLint GLES2DecoderTestBase::kInvalidUniformLocation;
    const GLint GLES2DecoderTestBase::kBadUniformIndex;

#endif

    const char* GLES2DecoderTestBase::kAttrib1Name = "attrib1";
    const char* GLES2DecoderTestBase::kAttrib2Name = "attrib2";
    const char* GLES2DecoderTestBase::kAttrib3Name = "attrib3";
    const char* GLES2DecoderTestBase::kUniform1Name = "uniform1";
    const char* GLES2DecoderTestBase::kUniform2Name = "uniform2[0]";
    const char* GLES2DecoderTestBase::kUniform3Name = "uniform3[0]";
    const char* GLES2DecoderTestBase::kUniform4Name = "uniform4";
    const char* GLES2DecoderTestBase::kUniform5Name = "uniform5";
    const char* GLES2DecoderTestBase::kUniform6Name = "uniform6";
    const char* GLES2DecoderTestBase::kUniform7Name = "uniform7";

    void GLES2DecoderTestBase::SetupDefaultProgram()
    {
        {
            static AttribInfo attribs[] = {
                {
                    kAttrib1Name,
                    kAttrib1Size,
                    kAttrib1Type,
                    kAttrib1Location,
                },
                {
                    kAttrib2Name,
                    kAttrib2Size,
                    kAttrib2Type,
                    kAttrib2Location,
                },
                {
                    kAttrib3Name,
                    kAttrib3Size,
                    kAttrib3Type,
                    kAttrib3Location,
                },
            };
            static UniformInfo uniforms[] = {
                { kUniform1Name, kUniform1Size, kUniform1Type,
                    kUniform1FakeLocation, kUniform1RealLocation,
                    kUniform1DesiredLocation },
                { kUniform2Name, kUniform2Size, kUniform2Type,
                    kUniform2FakeLocation, kUniform2RealLocation,
                    kUniform2DesiredLocation },
                { kUniform3Name, kUniform3Size, kUniform3Type,
                    kUniform3FakeLocation, kUniform3RealLocation,
                    kUniform3DesiredLocation },
                { kUniform4Name, kUniform4Size, kUniform4Type,
                    kUniform4FakeLocation, kUniform4RealLocation,
                    kUniform4DesiredLocation },
                { kUniform5Name, kUniform5Size, kUniform5Type,
                    kUniform5FakeLocation, kUniform5RealLocation,
                    kUniform5DesiredLocation },
                { kUniform6Name, kUniform6Size, kUniform6Type,
                    kUniform6FakeLocation, kUniform6RealLocation,
                    kUniform6DesiredLocation },
                { kUniform7Name, kUniform7Size, kUniform7Type,
                    kUniform7FakeLocation, kUniform7RealLocation,
                    kUniform7DesiredLocation },
            };
            SetupShader(attribs, arraysize(attribs), uniforms, arraysize(uniforms),
                client_program_id_, kServiceProgramId,
                client_vertex_shader_id_, kServiceVertexShaderId,
                client_fragment_shader_id_, kServiceFragmentShaderId);
        }

        {
            EXPECT_CALL(*gl_, UseProgram(kServiceProgramId))
                .Times(1)
                .RetiresOnSaturation();
            cmds::UseProgram cmd;
            cmd.Init(client_program_id_);
            EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        }
    }

    void GLES2DecoderTestBase::SetupCubemapProgram()
    {
        {
            static AttribInfo attribs[] = {
                {
                    kAttrib1Name,
                    kAttrib1Size,
                    kAttrib1Type,
                    kAttrib1Location,
                },
                {
                    kAttrib2Name,
                    kAttrib2Size,
                    kAttrib2Type,
                    kAttrib2Location,
                },
                {
                    kAttrib3Name,
                    kAttrib3Size,
                    kAttrib3Type,
                    kAttrib3Location,
                },
            };
            static UniformInfo uniforms[] = {
                {
                    kUniform1Name,
                    kUniform1Size,
                    kUniformCubemapType,
                    kUniform1FakeLocation,
                    kUniform1RealLocation,
                    kUniform1DesiredLocation,
                },
                {
                    kUniform2Name,
                    kUniform2Size,
                    kUniform2Type,
                    kUniform2FakeLocation,
                    kUniform2RealLocation,
                    kUniform2DesiredLocation,
                },
                {
                    kUniform3Name,
                    kUniform3Size,
                    kUniform3Type,
                    kUniform3FakeLocation,
                    kUniform3RealLocation,
                    kUniform3DesiredLocation,
                },
                {
                    kUniform4Name,
                    kUniform4Size,
                    kUniform4Type,
                    kUniform4FakeLocation,
                    kUniform4RealLocation,
                    kUniform4DesiredLocation,
                },
                { kUniform5Name, kUniform5Size, kUniform5Type,
                    kUniform5FakeLocation, kUniform5RealLocation,
                    kUniform5DesiredLocation },
                { kUniform6Name, kUniform6Size, kUniform6Type,
                    kUniform6FakeLocation, kUniform6RealLocation,
                    kUniform6DesiredLocation },
                { kUniform7Name, kUniform7Size, kUniform7Type,
                    kUniform7FakeLocation, kUniform7RealLocation,
                    kUniform7DesiredLocation },
            };
            SetupShader(attribs, arraysize(attribs), uniforms, arraysize(uniforms),
                client_program_id_, kServiceProgramId,
                client_vertex_shader_id_, kServiceVertexShaderId,
                client_fragment_shader_id_, kServiceFragmentShaderId);
        }

        {
            EXPECT_CALL(*gl_, UseProgram(kServiceProgramId))
                .Times(1)
                .RetiresOnSaturation();
            cmds::UseProgram cmd;
            cmd.Init(client_program_id_);
            EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        }
    }

    void GLES2DecoderTestBase::SetupSamplerExternalProgram()
    {
        {
            static AttribInfo attribs[] = {
                {
                    kAttrib1Name,
                    kAttrib1Size,
                    kAttrib1Type,
                    kAttrib1Location,
                },
                {
                    kAttrib2Name,
                    kAttrib2Size,
                    kAttrib2Type,
                    kAttrib2Location,
                },
                {
                    kAttrib3Name,
                    kAttrib3Size,
                    kAttrib3Type,
                    kAttrib3Location,
                },
            };
            static UniformInfo uniforms[] = {
                {
                    kUniform1Name,
                    kUniform1Size,
                    kUniformSamplerExternalType,
                    kUniform1FakeLocation,
                    kUniform1RealLocation,
                    kUniform1DesiredLocation,
                },
                {
                    kUniform2Name,
                    kUniform2Size,
                    kUniform2Type,
                    kUniform2FakeLocation,
                    kUniform2RealLocation,
                    kUniform2DesiredLocation,
                },
                {
                    kUniform3Name,
                    kUniform3Size,
                    kUniform3Type,
                    kUniform3FakeLocation,
                    kUniform3RealLocation,
                    kUniform3DesiredLocation,
                },
                {
                    kUniform4Name,
                    kUniform4Size,
                    kUniform4Type,
                    kUniform4FakeLocation,
                    kUniform4RealLocation,
                    kUniform4DesiredLocation,
                },
                { kUniform5Name, kUniform5Size, kUniform5Type,
                    kUniform5FakeLocation, kUniform5RealLocation,
                    kUniform5DesiredLocation },
                { kUniform6Name, kUniform6Size, kUniform6Type,
                    kUniform6FakeLocation, kUniform6RealLocation,
                    kUniform6DesiredLocation },
                { kUniform7Name, kUniform7Size, kUniform7Type,
                    kUniform7FakeLocation, kUniform7RealLocation,
                    kUniform7DesiredLocation },
            };
            SetupShader(attribs, arraysize(attribs), uniforms, arraysize(uniforms),
                client_program_id_, kServiceProgramId,
                client_vertex_shader_id_, kServiceVertexShaderId,
                client_fragment_shader_id_, kServiceFragmentShaderId);
        }

        {
            EXPECT_CALL(*gl_, UseProgram(kServiceProgramId))
                .Times(1)
                .RetiresOnSaturation();
            cmds::UseProgram cmd;
            cmd.Init(client_program_id_);
            EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        }
    }

    void GLES2DecoderWithShaderTestBase::TearDown()
    {
        GLES2DecoderTestBase::TearDown();
    }

    void GLES2DecoderTestBase::SetupShader(
        GLES2DecoderTestBase::AttribInfo* attribs, size_t num_attribs,
        GLES2DecoderTestBase::UniformInfo* uniforms, size_t num_uniforms,
        GLuint program_client_id, GLuint program_service_id,
        GLuint vertex_shader_client_id, GLuint vertex_shader_service_id,
        GLuint fragment_shader_client_id, GLuint fragment_shader_service_id)
    {
        {
            InSequence s;

            EXPECT_CALL(*gl_,
                AttachShader(program_service_id, vertex_shader_service_id))
                .Times(1)
                .RetiresOnSaturation();
            EXPECT_CALL(*gl_,
                AttachShader(program_service_id, fragment_shader_service_id))
                .Times(1)
                .RetiresOnSaturation();
            TestHelper::SetupShader(
                gl_.get(), attribs, num_attribs, uniforms, num_uniforms,
                program_service_id);
        }

        DoCreateShader(
            GL_VERTEX_SHADER, vertex_shader_client_id, vertex_shader_service_id);
        DoCreateShader(
            GL_FRAGMENT_SHADER, fragment_shader_client_id,
            fragment_shader_service_id);

        TestHelper::SetShaderStates(
            gl_.get(), GetShader(vertex_shader_client_id), true);
        TestHelper::SetShaderStates(
            gl_.get(), GetShader(fragment_shader_client_id), true);

        cmds::AttachShader attach_cmd;
        attach_cmd.Init(program_client_id, vertex_shader_client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(attach_cmd));

        attach_cmd.Init(program_client_id, fragment_shader_client_id);
        EXPECT_EQ(error::kNoError, ExecuteCmd(attach_cmd));

        cmds::LinkProgram link_cmd;
        link_cmd.Init(program_client_id);

        EXPECT_EQ(error::kNoError, ExecuteCmd(link_cmd));
    }

    void GLES2DecoderTestBase::DoEnableDisable(GLenum cap, bool enable)
    {
        SetupExpectationsForEnableDisable(cap, enable);
        if (enable) {
            cmds::Enable cmd;
            cmd.Init(cap);
            EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        } else {
            cmds::Disable cmd;
            cmd.Init(cap);
            EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
        }
    }

    void GLES2DecoderTestBase::DoEnableVertexAttribArray(GLint index)
    {
        EXPECT_CALL(*gl_, EnableVertexAttribArray(index))
            .Times(1)
            .RetiresOnSaturation();
        cmds::EnableVertexAttribArray cmd;
        cmd.Init(index);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoBufferData(GLenum target, GLsizei size)
    {
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, BufferData(target, size, _, GL_STREAM_DRAW))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .RetiresOnSaturation();
        cmds::BufferData cmd;
        cmd.Init(target, size, 0, 0, GL_STREAM_DRAW);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoBufferSubData(
        GLenum target, GLint offset, GLsizei size, const void* data)
    {
        EXPECT_CALL(*gl_, BufferSubData(target, offset, size, shared_memory_address_))
            .Times(1)
            .RetiresOnSaturation();
        memcpy(shared_memory_address_, data, size);
        cmds::BufferSubData cmd;
        cmd.Init(target, offset, size, shared_memory_id_, shared_memory_offset_);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::DoScissor(GLint x,
        GLint y,
        GLsizei width,
        GLsizei height)
    {
        EXPECT_CALL(*gl_, Scissor(x, y, width, height))
            .Times(1)
            .RetiresOnSaturation();
        cmds::Scissor cmd;
        cmd.Init(x, y, width, height);
        EXPECT_EQ(error::kNoError, ExecuteCmd(cmd));
    }

    void GLES2DecoderTestBase::SetupVertexBuffer()
    {
        DoEnableVertexAttribArray(1);
        DoBindBuffer(GL_ARRAY_BUFFER, client_buffer_id_, kServiceBufferId);
        DoBufferData(GL_ARRAY_BUFFER, kNumVertices * 2 * sizeof(GLfloat));
    }

    void GLES2DecoderTestBase::SetupAllNeededVertexBuffers()
    {
        DoBindBuffer(GL_ARRAY_BUFFER, client_buffer_id_, kServiceBufferId);
        DoBufferData(GL_ARRAY_BUFFER, kNumVertices * 16 * sizeof(float));
        DoEnableVertexAttribArray(0);
        DoEnableVertexAttribArray(1);
        DoEnableVertexAttribArray(2);
        DoVertexAttribPointer(0, 2, GL_FLOAT, 0, 0);
        DoVertexAttribPointer(1, 2, GL_FLOAT, 0, 0);
        DoVertexAttribPointer(2, 2, GL_FLOAT, 0, 0);
    }

    void GLES2DecoderTestBase::SetupIndexBuffer()
    {
        DoBindBuffer(GL_ELEMENT_ARRAY_BUFFER,
            client_element_buffer_id_,
            kServiceElementBufferId);
        static const GLshort indices[] = { 100, 1, 2, 3, 4, 5, 6, 7, 100, 9 };
        static_assert(arraysize(indices) == kNumIndices,
            "indices should have kNumIndices elements");
        DoBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices));
        DoBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, 2, indices);
        DoBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 2, sizeof(indices) - 2, &indices[1]);
    }

    void GLES2DecoderTestBase::SetupTexture()
    {
        DoBindTexture(GL_TEXTURE_2D, client_texture_id_, kServiceTextureId);
        DoTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
            kSharedMemoryId, kSharedMemoryOffset);
    };

    void GLES2DecoderTestBase::DeleteVertexBuffer()
    {
        DoDeleteBuffer(client_buffer_id_, kServiceBufferId);
    }

    void GLES2DecoderTestBase::DeleteIndexBuffer()
    {
        DoDeleteBuffer(client_element_buffer_id_, kServiceElementBufferId);
    }

    void GLES2DecoderTestBase::AddExpectationsForSimulatedAttrib0WithError(
        GLsizei num_vertices, GLuint buffer_id, GLenum error)
    {
        if (group_->feature_info()->gl_version_info().BehavesLikeGLES()) {
            return;
        }

        EXPECT_CALL(*gl_, GetError())
            .WillOnce(Return(GL_NO_ERROR))
            .WillOnce(Return(error))
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, kServiceAttrib0BufferId))
            .Times(1)
            .RetiresOnSaturation();
        EXPECT_CALL(*gl_, BufferData(GL_ARRAY_BUFFER, num_vertices * sizeof(GLfloat) * 4, _, GL_DYNAMIC_DRAW))
            .Times(1)
            .RetiresOnSaturation();
        if (error == GL_NO_ERROR) {
            EXPECT_CALL(*gl_, BufferSubData(GL_ARRAY_BUFFER, 0, num_vertices * sizeof(GLfloat) * 4, _))
                .Times(1)
                .RetiresOnSaturation();
            EXPECT_CALL(*gl_, VertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, NULL))
                .Times(1)
                .RetiresOnSaturation();
            EXPECT_CALL(*gl_, VertexAttribDivisorANGLE(0, 0))
                .Times(testing::AtMost(1))
                .RetiresOnSaturation();
            EXPECT_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, buffer_id))
                .Times(1)
                .RetiresOnSaturation();
        }
    }

    void GLES2DecoderTestBase::AddExpectationsForSimulatedAttrib0(
        GLsizei num_vertices, GLuint buffer_id)
    {
        AddExpectationsForSimulatedAttrib0WithError(
            num_vertices, buffer_id, GL_NO_ERROR);
    }

    void GLES2DecoderTestBase::SetupMockGLBehaviors()
    {
        ON_CALL(*gl_, BindVertexArrayOES(_))
            .WillByDefault(Invoke(
                &gl_states_,
                &GLES2DecoderTestBase::MockGLStates::OnBindVertexArrayOES));
        ON_CALL(*gl_, BindBuffer(GL_ARRAY_BUFFER, _))
            .WillByDefault(WithArg<1>(Invoke(
                &gl_states_,
                &GLES2DecoderTestBase::MockGLStates::OnBindArrayBuffer)));
        ON_CALL(*gl_, VertexAttribPointer(_, _, _, _, _, NULL))
            .WillByDefault(InvokeWithoutArgs(
                &gl_states_,
                &GLES2DecoderTestBase::MockGLStates::OnVertexAttribNullPointer));
    }

    GLES2DecoderWithShaderTestBase::MockCommandBufferEngine::
        MockCommandBufferEngine()
    {

        scoped_ptr<base::SharedMemory> shm(new base::SharedMemory());
        shm->CreateAndMapAnonymous(kSharedBufferSize);
        valid_buffer_ = MakeBufferFromSharedMemory(shm.Pass(), kSharedBufferSize);

        ClearSharedMemory();
    }

    GLES2DecoderWithShaderTestBase::MockCommandBufferEngine::
        ~MockCommandBufferEngine() { }

    scoped_refptr<gpu::Buffer>
    GLES2DecoderWithShaderTestBase::MockCommandBufferEngine::GetSharedMemoryBuffer(
        int32 shm_id)
    {
        return shm_id == kSharedMemoryId ? valid_buffer_ : invalid_buffer_;
    }

    void GLES2DecoderWithShaderTestBase::MockCommandBufferEngine::set_token(
        int32 token)
    {
        DCHECK(false);
    }

    bool GLES2DecoderWithShaderTestBase::MockCommandBufferEngine::SetGetBuffer(
        int32 /* transfer_buffer_id */)
    {
        DCHECK(false);
        return false;
    }

    bool GLES2DecoderWithShaderTestBase::MockCommandBufferEngine::SetGetOffset(
        int32 offset)
    {
        DCHECK(false);
        return false;
    }

    int32 GLES2DecoderWithShaderTestBase::MockCommandBufferEngine::GetGetOffset()
    {
        DCHECK(false);
        return 0;
    }

    void GLES2DecoderWithShaderTestBase::SetUp()
    {
        GLES2DecoderTestBase::SetUp();
        SetupDefaultProgram();
    }

// Include the auto-generated part of this file. We split this because it means
// we can easily edit the non-auto generated parts right here in this file
// instead of having to edit some template or the code generator.
#include "gpu/command_buffer/service/gles2_cmd_decoder_unittest_0_autogen.h"

} // namespace gles2
} // namespace gpu
